import {
  _decorator,
  Component,
  SpriteFrame,
  resources,
  director,
  Node,
  Sprite,
  Vec3,
  UITransform,
  tween,
  ResolutionPolicy,
  view,
} from 'cc';
import {
  PlantAction,
  CommonData,
  plantNames,
  ZombieAction,
  zombieNames,
} from './Utils/CommonData';
const { ccclass, property } = _decorator;

@ccclass('Loading')
export class Loading extends Component {
  @property(Node)
  progress: Node = null;

  @property(Node)
  zm: Node = null;

  frames: SpriteFrame[] = [];

  private frameRate: number = 0.1;
  private _frameIndex: number = 0;
  private _timer: number = 0;
  private zmSprite: Sprite = null;

  protected async start() {
    const designWidth = 1280;
    const desingHeight = 720;
    view.setDesignResolutionSize(
      designWidth,
      desingHeight,
      ResolutionPolicy.FIXED_HEIGHT
    );
    if (this.zm) {
      this.frames = await this.zombieLoadFrame('normal', 'run');
      this.zm.scale = new Vec3(
        -Math.abs(this.zm.scale.x),
        this.zm.scale.y,
        this.zm.scale.z
      );
      this.zm.active = true;
      this.zmSprite = this.zm.getComponent(Sprite);
      console.log(this.zm.position);
    }
    await this.loadAllResources();
    director.loadScene('home')
    // director.loadScene('zombiedebug');
  }

  protected update(dt: number): void {
    const pos = this.zm.position;
    this.updateAnimation(dt);
    // this.zm.setPosition(pos.x)
  }

  updateAnimation(dt: number) {
    if (this.frames && this.frames.length) {
      this._timer += dt;
      if (this._timer >= this.frameRate) {
        this._timer = 0;
        this._frameIndex++;
        if (this._frameIndex >= this.frames.length) {
          this._frameIndex = 0;
        } else {
          this.zmSprite.spriteFrame = this.frames[this._frameIndex];
        }
      }
    }
  }

  private async loadAllResources() {
    const sunCount = 1; // 太阳动作
    const totalCount =
      plantNames.length * 3 + // 每个植物3个动作
      zombieNames.length * 6 + // 每个僵尸6个动作
      sunCount;

    let loadedCount = 0;

    const actions: PlantAction[] = ['idle', 'attack', 'digest'];
    for (let plantName of plantNames) {
      const plantMap: Map<PlantAction, SpriteFrame[]> = new Map();
      for (let act of actions) {
        const frames = await this.plantLoadFrames(plantName, act);
        plantMap.set(act, frames);
        loadedCount++;
        await this.updateProgress(loadedCount / totalCount);
        await this.yieldToNextFrame();
      }
      CommonData.instance.plantFramesMap.set(plantName, plantMap);
    }
    console.log('植物序列帧加载完成');

    const zActions: ZombieAction[] = [
      'attack',
      'die',
      'dieboom',
      'dying',
      'idle',
      'run',
    ];
    for (let zombieName of zombieNames) {
      const zombieMap: Map<ZombieAction, SpriteFrame[] | SpriteFrame[][]> =
        new Map();
      for (let act of zActions) {
        if (act === 'dying') {
          const frames = await this.zombieLoadFramesByHeaderAndBody(
            zombieName,
            act
          );
          zombieMap.set(act, frames);
        } else {
          const frames = await this.zombieLoadFrame(zombieName, act);
          zombieMap.set(act, frames);
        }
        loadedCount++;
        this.updateProgress(loadedCount / totalCount);
        await this.yieldToNextFrame();
      }
      CommonData.instance.zombieFramesMap.set(zombieName, zombieMap);
    }
    console.log('僵尸序列帧加载完成');

    const sunFrames = await this.sunLoadFrame();
    loadedCount++;
    CommonData.instance.sunFrames = sunFrames;
    console.log('太阳序列帧加载完成');
  }

  private plantLoadFrames(plantName: string, action: PlantAction) {
    return new Promise<SpriteFrame[]>((resolve) => {
      const path = `texture/plants/${plantName}/${action}`;
      resources.loadDir(path, SpriteFrame, (err, assets) => {
        if (err) {
          console.warn(`加载${plantName}/${action}失败`, err);
          resolve([]);
          return;
        }
        resolve(assets as SpriteFrame[]);
      });
    });
  }

  private zombieLoadFramesByHeaderAndBody(
    zombieName: string,
    action: ZombieAction
  ) {
    return new Promise<SpriteFrame[][]>((resolve) => {
      const dirs = [
        `texture/zombie/${zombieName}/${action}/head`,
        `texture/zombie/${zombieName}/${action}/body`,
      ];
      const assetsRes = dirs.map((path) => {
        return new Promise<SpriteFrame[]>((resolve) => {
          resources.loadDir(path, SpriteFrame, (err, assets) => {
            if (err) {
              console.warn(`加载${path}失败`, err);
              resolve([]);
              return;
            }
            resolve(assets as SpriteFrame[]);
          });
        });
      });
      Promise.all(assetsRes).then((assets) => {
        resolve(assets as SpriteFrame[][]);
      });
    });
  }

  private zombieLoadFrame(zombieName: string, action: ZombieAction) {
    return new Promise<SpriteFrame[]>((resolve) => {
      const path = `texture/zombie/${zombieName}/${action}`;
      resources.loadDir(path, SpriteFrame, (err, assets) => {
        if (err) {
          console.warn(`加载${zombieName}/${action}失败`, err);
          resolve([]);
          return;
        }
        resolve(assets as SpriteFrame[]);
      });
    });
  }

  private sunLoadFrame() {
    return new Promise<SpriteFrame[]>((resolve) => {
      const path = `texture/sun`;
      resources.loadDir(path, SpriteFrame, (err, assets) => {
        if (err) {
          console.error(`加载太阳失败`, err);
          resolve([]);
          return;
        }
        resolve(assets as SpriteFrame[]);
      });
    });
  }

  private async updateProgress(progress: number) {
    if (!this.progress) return;
    const uiTrans = this.progress.getComponent(UITransform);
    if (!uiTrans) return;
    const width = uiTrans.width;
    const leftX = -width * uiTrans.anchorX;
    const newX = leftX + progress * width;
    await new Promise<void>((resolve) => {
      tween(this.zm)
        .to(0.1, {
          position: new Vec3(newX, this.zm.position.y, this.zm.position.z),
        })
        .call(() => resolve())
        .start();
    });
  }

  private yieldToNextFrame(): Promise<void> {
    return new Promise((resolve) => setTimeout(resolve, 0));
  }
}
